Imports System.Collections.ObjectModel
Imports System.ComponentModel

Namespace Collections

    ''' <summary>
    ''' Represents a Collection of XElements.
    ''' </summary>
    ''' <typeparam name="TType">The type of the items in the collection.</typeparam>
    Public Class XElementCollection(Of TType As XElement)
        Implements IList(Of TType)
        Implements IList

        Private __Parent As XElement
        Private __ElementName As XName

        ''' <summary>Initializes an instance of the <see cref="XElementCollection" /> class.</summary>
        ''' <param name="parent">The <see cref="XElement"/> that is the owner of this instance.</param>
        ''' <param name="elementName">The name of the item element.</param>
        Public Sub New(ByVal parent As XElement, ByVal elementName As XName)
            __Parent = parent
            __ElementName = elementName
        End Sub

        ''' <summary>Initializes an instance of the <see cref="XElementCollection" /> class.</summary>
        ''' <param name="parent">The <see cref="XElement"/> that is the owner of this instance.</param>
        ''' <param name="namespace">The <see cref="XNamespace"/> of the item element.</param>
        ''' <param name="elementName">The name of the item element.</param>
        Public Sub New(ByVal parent As XElement, ByVal [namespace] As XNamespace, ByVal elementName As String)
            __Parent = parent
            __ElementName = [namespace] + elementName
        End Sub

        ''' <summary>
        ''' Gets the parent of the collection.
        ''' </summary>
        Public ReadOnly Property Parent() As XElement
            Get
                Return __Parent
            End Get
        End Property

        ''' <summary>
        ''' Gets the <see cref="XName"/> of the item element.
        ''' </summary>
        Public ReadOnly Property ElementName() As XName
            Get
                Return __ElementName
            End Get
        End Property

        ''' <summary>
        ''' Adds a new element into the collection.
        ''' </summary>
        ''' <param name="item">The item to be added.</param>
        Public Overridable Sub Add(ByVal item As TType) Implements System.Collections.Generic.ICollection(Of TType).Add
            Me.Parent.Add(item)
        End Sub

        ''' <summary>
        ''' Remove all elements from the <see cref="XElementCollection(Of T)"/>.
        ''' </summary>
        Public Overridable Sub Clear() Implements System.Collections.Generic.ICollection(Of TType).Clear, System.Collections.IList.Clear
            For Each n As XNode In Me.GetElements()
                n.Remove()
            Next
        End Sub

        ''' <summary>
        ''' Determines whether the <see cref="XElementCollection(Of T)"/> contains a specific value.
        ''' </summary>
        ''' <param name="item">The object to locate in the <see cref="XElementCollection(Of T)"/>.</param>
        ''' <returns><c>True</c> if the item is found in the <see cref="XElementCollection(Of T)"/>; <c>False</c> otehrwise.</returns>
        Public Overridable Function Contains(ByVal item As TType) As Boolean Implements System.Collections.Generic.ICollection(Of TType).Contains
            Return Me.GetElements().Contains(item)
        End Function

        ''' <summary>
        ''' Copies the elements of the <see cref="XElementCollection(Of T)"/> to an <see cref="Array"/>, starting at a particular index.
        ''' </summary>
        ''' <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from System.Collections.Generic.ICollection(Of T). The <see cref="Array"/> must have zero-based indexing.</param>
        ''' <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        Public Overridable Sub CopyTo(ByVal array() As TType, ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of TType).CopyTo
            Me.GetElements().CopyTo(array, arrayIndex)
        End Sub

        ''' <summary>
        ''' Gets the number of elements contained in the System.Collections.Generic.ICollection(Of T).
        ''' </summary>
        ''' <returns>The number of elements contained in the System.Collections.Generic.ICollection(Of T).</returns>
        Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of TType).Count, System.Collections.ICollection.Count
            Get
                Return Me.GetElements().Length
            End Get
        End Property

        ''' <summary>
        ''' Gets a value indicating whether the System.Collections.Generic.ICollection(Of T) is read-only.
        ''' </summary>
        Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of TType).IsReadOnly, System.Collections.IList.IsReadOnly
            Get
                Return False
            End Get
        End Property

        ''' <summary>
        ''' Removes the first occurrence of a specific object from the System.Collections.Generic.ICollection(Of T).
        ''' </summary>
        ''' <param name="item">The object to remove from the System.Collections.Generic.ICollection(Of T).</param>
        ''' <returns><c>True</c> if item was successfully removed from the System.Collections.Generic.ICollection(Of T); otherwise, <c>False</c>. This method also returns <c>False</c> if item is not found in the original System.Collections.Generic.ICollection(Of T).</returns>
        Public Overridable Function Remove(ByVal item As TType) As Boolean Implements System.Collections.Generic.ICollection(Of TType).Remove
            If (Me.Contains(item)) Then
                Me.Parent.Element(item.Name).Remove()
                Return True
            End If
            Return False
        End Function

        ''' <summary>
        ''' Returns an enumerator that iterates through the collection.
        ''' </summary>
        ''' <returns>A <see cref="System.Collections.Generic.IEnumerator(Of T)"/> that can be used to iterate through the collection.</returns>
        Public Overridable Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TType) Implements System.Collections.Generic.IEnumerable(Of TType).GetEnumerator
            Return New XElementCollectionEnumerator(Of TType)(Me.GetElements())
        End Function

        ''' <summary>
        ''' Determines the index of a specific item in the System.Collections.Generic.IList(Of T).
        ''' </summary>
        ''' <param name="item">The object to locate in the System.Collections.Generic.IList(Of T).</param>
        ''' <returns>The index of item if found in the list; otherwise, -1.</returns>
        Public Overridable Function IndexOf(ByVal item As TType) As Integer Implements System.Collections.Generic.IList(Of TType).IndexOf
            Return Array.IndexOf(Me.GetElements(), item)
        End Function

        ''' <summary>
        ''' Inserts an item to the System.Collections.Generic.IList(Of T) at the specified index.
        ''' </summary>
        ''' <param name="index">The zero-based index at which item should be inserted.</param>
        ''' <param name="item">The object to insert into the System.Collections.Generic.IList(Of T).</param>
        Public Overridable Sub Insert(ByVal index As Integer, ByVal item As TType) Implements System.Collections.Generic.IList(Of TType).Insert
            Dim arr = Me.GetElements()
            If (index <= 0) Then
                arr(0).AddBeforeSelf(item)
            ElseIf (index >= arr.Length) Then
                arr(arr.Length - 1).AddAfterSelf(item)
            Else
                arr(index).AddBeforeSelf(item)
            End If
        End Sub

        ''' <summary>
        ''' Gets or sets the element at the specified index.
        ''' </summary>
        ''' <param name="index">The zero-based index of the element to get or set.</param>
        ''' <value>The element at the specified index.</value>
        Default Public Property Item(ByVal index As Integer) As TType Implements System.Collections.Generic.IList(Of TType).Item
            Get
                Return Me.Elements(index)
            End Get
            Set(ByVal value As TType)
                Me.Elements(index).Remove()
                Me.Insert(index, value)
            End Set
        End Property

        ''' <summary>
        ''' Removes the System.Collections.Generic.IList(Of T) item at the specified index.
        ''' </summary>
        ''' <param name="index">The zero-based index of the item to remove.</param>
        Public Overridable Sub RemoveAt(ByVal index As Integer) Implements System.Collections.Generic.IList(Of TType).RemoveAt
            Me.Elements(index).Remove()
        End Sub

        ''' <summary>
        ''' Returns an array of <see cref="XElement"/> that contains all the matching elements name.
        ''' </summary>
        Private Function GetElements() As XElement()
            Return Me.Parent.Elements.Where(Function(x) x.Name = Me.ElementName).ToArray()
        End Function

#Region " IEnumerable Implemenation "

        Private Function IEnumerableGetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return Me.GetElements().GetEnumerator()
        End Function

#End Region

#Region " IList Implementation "

        Private Sub IListCopyTo(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo
            Me.CopyTo(array, index)
        End Sub

        <Browsable(False)> _
        <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
        Private ReadOnly Property IsSynchronized() As Boolean Implements System.Collections.ICollection.IsSynchronized
            Get
                Return False
            End Get
        End Property

        <Browsable(False)> _
        <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
        Private ReadOnly Property SyncRoot() As Object Implements System.Collections.ICollection.SyncRoot
            Get
                Return Me
            End Get
        End Property

        Private Function IListAdd(ByVal value As Object) As Integer Implements System.Collections.IList.Add
            Me.Add(value)
            Return Me.IndexOf(value)
        End Function

        Private Function IListContains(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
            Return Me.Contains(value)
        End Function

        Private Function IListIndexOf(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
            Return Me.IndexOf(value)
        End Function

        Private Sub IListInsert(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
            Me.Insert(index, value)
        End Sub

        <Browsable(False)> _
        <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
        Private ReadOnly Property IsFixedSize() As Boolean Implements System.Collections.IList.IsFixedSize
            Get
                Return False
            End Get
        End Property

        <Browsable(False)> _
        <DebuggerBrowsable(DebuggerBrowsableState.Never)> _
        Private Overloads Property IListItem(ByVal index As Integer) As Object Implements System.Collections.IList.Item
            Get
                Return Me.Item(index)
            End Get
            Set(ByVal value As Object)
                Me.Item(index) = value
            End Set
        End Property

        Private Sub IListRemove(ByVal value As Object) Implements System.Collections.IList.Remove
            Me.Remove(value)
        End Sub

        Private Sub IListRemoveAt(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
            Me.RemoveAt(index)
        End Sub

#End Region

    End Class

End Namespace
